<?php
/**
 * Created by PhpStorm.
 * User: Inhere
 * Date: 2015/2/28
 * Use : ...
 * File: Model.php
 */

namespace ulue\web;

use ulue\core\db\base\query\AbstractDataQuery;
use ulue\core\db\QueryElement;
use ulue\core\model\DatabaseModel;

class Model extends DatabaseModel
{

    // 表主键字段(旧的)值
    protected $_priKey = null;

    public function auto($flag=false)
    {
        # code...
    }

    public function rules()
    {
        return [];
    }

    /**
     * Declares attribute labels.
     */
    public function attrLabels()
    {
        return array(
            // 'name'=>'名称：',
        );
    }

    public function hasAttr($name)
    {
        return isset($this->_attrs[$name]) || in_array( $name, $this->attrNames() );
    }

    public function setAttr($name, $value=null) // setAttr
    {
        if(property_exists($this,$name)) {
            $this->$name=$value;
        } else if ( in_array($name, $this->attrNames() ) ) {
            $this->_attrs[$name] = (string)$value;
        } else {
            return false;
            // throw new \DMissArgumentException( sprintf('模型 %s 不存在字段 %s', get_class($this), $name));
        }

        return $this;
    }

    /**
     * 获取模型属性
     * @param $name
     * @throws \DNotFoundException
     * @return mixed
     */
    public function getAttr($name) //getAttr
    {
        if (!is_string($name) || !$name) {
            return null;
        }

        $name = trim($name);

        if(property_exists($this,$name)) {
            return $this->$name;
        } else if ( isset($this->_attrs[$name]) ) {
            return $this->_attrs[$name];
        }

        throw new \DNotFoundException( sprintf('模型 %s 不存在字段 %s', get_class($this), $name));
    }

    /**
     * 判断两个模型是否相同
     * @param DatabaseModel $model
     * @return bool
     */
    public function equals(DatabaseModel $model)
    {
        if ($this->getIsNew() || $model->getIsNew()) {
            return false;
        }

        return static::tableName()===$model::tableName() && $this->getPriKey()===$model->getPriKey();
    }

    /**
     * 得到主键值，在查询到记录并载入模型数据后
     * @param bool $toString 当仅有一个主键列时，直接返回字符串列名
     * @return mixed|null
     */
    public function getPriKey($toString=true)
    {
        $pkColumn    = (array) static::priKeyName();

        if( count($pkColumn) == 1 && $toString ) {
            return $this->$pkColumn[0];
        }

        $values = [];

        foreach($pkColumn as $name) {
            $values[$name]=$this->$name;
        }

        return $values;
    }

    /**
     * 设置当前模型记录的主键值；在创建记录后
     * @param mixed $value
     * @return $this
     */
    public function setPriKey($value)
    {
        // 将旧的主键值保存
        $this->_priKey = $this->getPriKey();
        $pkColumn      = (array) static::priKeyName();

        if(is_string($value)) {
            $this->$pkColumn[0] = $value;
        } else if (is_array($value)) {
            foreach($pkColumn as $name) {
                $this->$name=$value[$name];
            }
        }

        return $this;
    }

    /**
     * 获取旧的主键值
     * @return mixed
     */
    public function getOldPriKey()
    {
        return $this->_priKey;
    }

    /**
     * 设置旧的主键值
     * @param mixed $value the old primary key value.
     * @return $this
     */
    public function setOldPriKey($value)
    {
        $this->_priKey = $value;

        return $this;
    }

    /**
     * 得到主键值，在查询到记录并载入模型数据后
     * @return mixed|null
     */
    public function getForeKey()
    {
        $fkColumn    = $this->getTableSchema()->priKey;

        if( is_string($fkColumn) ) {
            return $this->$fkColumn;

        } // else if( is_array($fkColumn) )
        // {
        //     $values=[];

        //     foreach($fkColumn as $name)
        //     {
        //         $values[$name]=$this->$name;
        //     }

        //     return $values;
        // }

        return null;
    }

    /**
     * @param $name
     * @param $value
     * @throws \ulue\core\TheSystemException
     */
    public function __set($name,$value)
    {
        if( !$this->setAttr($name,$value) ) {
            parent::__set($name,$value);
        }

    }

    /**
     * @param $name
     * @return mixed|null
     * @throws \ulue\core\TheSystemException
     */
    public function __get($name)
    {
        if (isset($this->_attrs[$name])) {
            return $this->_attrs[$name];
        } else if (isset($this->getTableSchema()->columns[$name])) {
            return null;
        }

        return parent::__get($name);
    }

////////////////////////////////////////// event //////////////////////////////////////////


    /**
     * @return bool
     */
    public function beforeFind()
    {
        return true;
    }

    /**
     * @return void
     */
    public function afterFind()
    {
        # code...
    }

    public function beforeSave()
    {
        return true;
    }

    public function afterSave()
    {
    }

    public function beforeDelete()
    {
        return true;
    }

    public function afterDelete()
    {
    }


////////////////////////////////////////// find record //////////////////////////////////////////

    /**
     * @param string $condition
     * @param array $param
     * @return AbstractDataQuery
     */
    static public function find($condition='', array $param=[])
    {
        $query = static::getQuery(true);

        $query->setModelClass( get_called_class() );

        return $query->from()->where($condition, $param);
    }

    /**
     * 查询 -- 单行数据
     * @param string $condition
     * @param array|string $param - 条件字符串
     * @throws \DMissArgumentException
     * @return array
     */
    static public function findOne($condition='', array $param=[])
    {
        return static::find($condition, $param)->one();
    }

    /**
     * 查询 -- 多行数据
     * @param string $condition
     * @param array $param
     * @throws \DMissArgumentException
     * @return array
     */
    static public function findAll($condition='', array $param=[])
    {
        return static::find($condition, $param)->all();
    }

    //传入多个时，将采用 in()
    /**
     * @param string $pkValue 主键值；有多个主键时则是前导列主键值
     * @param string $condition 当有多个主键时， 后面可在添加非前导列主键值
     * @param array $param
     * @return array
     */
    static public function findByPk($pkValue, $condition='', array $param=[])
    {
        $query   = static::find();

        // first
        $pkName = $query->qn( static::priKeyName()[0] );

        // 查询多个
        if ( ($isArr = is_array($pkValue)) || strpos($pkValue, ',')) {
            $priKeys = $isArr ? implode(',', $pkValue) : $pkValue;

            $query->addWhere($condition, $param)->in($pkName, $priKeys );

            return $query->all();
        }

        return $query->where("{$pkName}='{$pkValue}'")->one();
    }

    static public function findOneBySql($sql, array $params=[])
    {
        return static::getDb()->setSql($sql)->setParams($params)->queryOne();
    }

    static public function findAllBySql($sql, array $params=[])
    {
        return  static::getDb()->setSql($sql)->setParams($params)->queryAll();
    }

    // 得到以 $field 来排序的第一条记录
    static public function findFirst($condition, array $param=[], $orderBy=null)
    {
        return '';
    }

    // 得到以 $field 来排序的最后一条记录
    static public function findLast($condition, array $param=[], $orderBy=null)
    {
        return '';
    }


////////////////////////////////////////// check record //////////////////////////////////////////

    static public function count($condition='', array $params=[])
    {}

    static public function countBySql($sql, array $params=[])
    {}

    static public function exists($condition='', array $params=[])
    {}

    // with
    public function link($value='')
    {
        # code...
    }

////////////////////////////////////////// insert record //////////////////////////////////////////

    /**
     * @param array $attrs
     * @return mixed successful--return insert id; failure -- return false
     * @throws \DLogicException
     */
    final public function insertRecord(array $attrs)
    {
        $query  = static::getQuery();
        $sql  = $query->buildCreate($attrs);

        $affectedRows = $query->db->exec($sql);

        if ($affectedRows) {
            return $query->db->getInsertId();
        }

        return false;
    }

    /**
     * 创建保存当前(记录)模型属性数据
     * @param array $attrs 可以选择性的插入列数据
     * @throws \DLogicException
     * @return bool
     */
    final public function insert(array $attrs=[])
    {
        if (!$this->getIsNew()) {
            throw new \DLogicException('已经存在的记录不能再次插入！');
        }

        if ($this->beforeSave()) {
            $pk = $this->getUniquePk();

            $insertId = $this->insertRecord( $this->getAttrs($attrs,[$pk]) );

            $this->_priKey = $insertId;
            $this->setPriKey($insertId);

            $this->afterSave();
            $this->setScenario('update')->setIsNew(false);

            return true;
        }

        return false;
    }


    public function insertAll($data)
    {
        $ids = [];

        foreach ($data as $attrs) {
            $ids[] = $this->insertRecord($attrs);
        }

        return $ids;
    }

    /**
     * 保存(当前模型对应表)数据，属性数据不一定是当前模型的数据
     * 注：更新数据时 必须传入主键值, 否则将可能造成插入新记录
     * @param  array $attrs
     * @param  null|string $scenario null 'insert' 'update' 为空时自动判断
     * @param  bool $validation 是否验证
     * @throws \DLogicException
     * @throws \DMissArgumentException
     * @return mixed
     */
    public function autoSave(array $attrs, $scenario= null, $validation=true)
    {
        $pkName = static::priKeyName()[0];

        if (!$scenario) {
            // 判断 要保存的记录是否存在(当前表)主键值
            $scenario = empty($attrs[$pkName]) ? 'insert' : 'update';
        }

        if ($scenario == 'insert') {
            return $this->insertRecord($attrs);

        } else if ($scenario == 'update') {
            if ( !($priKeyValue = $attrs[$pkName])) {
                throw new \DLogicException(__METHOD__ . '只能根据主键值更新数据，请传入主键['.$pkName.']对应值');
            }

            return $this->updateByPk( $priKeyValue, $attrs);
        }

        throw new \DMissArgumentException('第二个参数错误, 允许设置 \'insert\' | \'update\'，留空则自动判段。');
    }

    /**
     * 保存当前模型数据
     * @param bool $validation
     * @param  array $attrs
     * @throws \DLogicException
     * @return mixed
     */
    public function save($validation = true, array $attrs=[])
    {
        if ( !$validation || $this->validate($attrs) ) {
            return $this->getIsNew() ? $this->insert($attrs) : $this->update($attrs);
        }

        // 验证失败
        return false;
    }

    public function saveAttrs(array $attrs)
    {
        # code...
    }


////////////////////////////////////////// update record //////////////////////////////////////////

    /**
     * 查找 当前记录模型对应的 数据表记录的最新数据 重新载入到到模型
     * @return bool
     */
    public function reload()
    {
        if( $model=$this->findByPk($this->getPriKey()) ) {
            $this->_attrs = $this->_related =[];

            foreach($this->getColumns() as $name=>$column) {
                if(property_exists($this,$name)) {
                    $this->$name = $model->$name;
                } else {
                    $this->_attrs[$name] = $model->$name;
                }
            }

            return true;
        }

        return false;
    }

    /**
     * 更新保存当前(记录)模型属性数据
     * @param array $attrs
     * @throws \DLogicException
     * @return bool
     */
    final public function update(array $attrs=[])
    {
        if ( $this->getIsNew() ) {
            throw new \DLogicException('还没有保存的新记录不能够更新！');
        }

        if ($this->beforeSave()) {
            if( $this->_priKey===null ) {
                $this->_priKey = $this->getPriKey();
            }

            $attrs = $this->getAttrs($attrs, [ $this->getUniquePk() ]);

            $this->updateByPk($this->_priKey, $attrs);

            // 更新后，当前的主键值又成为旧值了
            $this->_priKey = $this->getPriKey();
            $this->afterSave();

            return true;
        }

        return false;
    }

    final public function updateRecord(array $attrs, $condition, $param=[] )
    {
        return static::getQuery()->update($attrs, $condition,$param);
    }

    public function updateAll(array $data,$condition, $param=[] )
    {
        return static::getQuery()->update($data,$condition,$param);
    }

    public function updateByPk($pkValue, array $attrs, $condition='', $param=[] )
    {
        $query  = static::getQuery();
        $pkName = $query->qn( static::priKeyName()[0] );

        $query->where( $pkName . '='. $pkValue )->addWhere($condition,$param);

        $sql    = $query->buildUpdate($attrs);

        return $query->db->exec($sql);
    }

////////////////////////////////////////// delete record //////////////////////////////////////////

    /**
     * 删除 -- 删除数据
     * @throws \DLogicException
     * @return int
     */
    final public function delete()
    {
        if ($this->getIsNew()) {
            throw new \DLogicException("不能删除还未保存过的新纪录");
        }

        return static::deleteByPk($this->getPriKey());
    }

    public function deleteRecord()
    {
        return $this->delete();
    }

    static public function deleteOne($condition, array $param=[] )
    {
        return static::getDb()->delete($condition);
    }

    static public function deleteAll($condition, array $param=[] )
    {
        return static::getDb()->delete($condition, $param );
    }

    static public function deleteByPk($pkValue, $condition='', array $param=[] )
    {
        $query  = static::getQuery();
        $pkName = $query->qn( static::priKeyName()[0] );

        $sql    = $query->where( $pkName . '='. $pkValue)->buildDelete();

        return $query->db->exec( $sql );
    }



}// end class Model